package com.stars.easyms.datasource.properties;

import com.stars.easyms.base.util.BeanPropertyUtil;
import com.stars.easyms.base.util.PropertyPlaceholderUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.lang.Nullable;

import java.util.*;
import java.util.function.Consumer;
import java.util.function.Supplier;

/**
 * <p>className: BasePropertiesProcessor</p>
 * <p>description: 属性处理基础类</p>
 *
 * @author guoguifang
 * @version 1.2.1
 * @date 2019-07-09 13:34
 */
@SuppressWarnings("unchecked")
public final class BasePropertiesProcessor {

    private static PropertiesPropertySource propertiesPropertySource;

    public static void setPropertiesPropertySource(PropertiesPropertySource propertiesPropertySource) {
        BasePropertiesProcessor.propertiesPropertySource = propertiesPropertySource;
    }

    static <T> void bindProperty(String propertyName, String standbyPropertyName, Consumer<T> consumer, Class<T> clazz) {
        String result = getPropertyValue(null, propertyName);
        if (result == null) {
            result = getPropertyValue(null, standbyPropertyName);
        }
        if (result != null) {
            bind(result, consumer, clazz);
        }
    }

    static String getPropertyValue(String propertyName) {
        String result;
        Object propertyValue = propertiesPropertySource.getProperty(propertyName);
        if (propertyValue == null) {
            result = PropertyPlaceholderUtil.replace(propertyName, null);
        } else {
            result = propertyValue.toString();
        }
        return result != null ? result.trim() : null;
    }

    private String prefix;

    BasePropertiesProcessor(String prefix) {
        this.prefix = prefix;
    }

    <T> void bindProperty(String propertyName, Consumer<T> consumer, T defaultValue) {
        String result = getPropertyValue(prefix, propertyName);
        if (result != null) {
            bind(result, consumer, defaultValue.getClass());
        } else if (defaultValue != null) {
            consumer.accept(defaultValue);
        }
    }

    <T> T bindProperty(String propertyName, Consumer<T> consumer, Class<T> clazz, Supplier<T> defaultValueSupplier) {
        String result = getPropertyValue(prefix, propertyName);
        if (result != null) {
            return bind(result, consumer, clazz);
        } else if (defaultValueSupplier != null) {
            T defaultValue = defaultValueSupplier.get();
            consumer.accept(defaultValue);
            return defaultValue;
        }
        return null;
    }

    <T> void bindProperty(Consumer<T> consumer, T defaultValue) {
        consumer.accept(defaultValue);
    }

    @Nullable
    private static String getPropertyValue(String prefix, String propertyName) {
        String result = getPropertyValue(prefix == null ? propertyName : prefix + "." + propertyName);
        if (result == null) {
            String dashedFormPropertyName = BeanPropertyUtil.toDashedForm(propertyName);
            if (!propertyName.equals(dashedFormPropertyName)) {
                result = getPropertyValue(prefix == null ? dashedFormPropertyName : prefix + "." + dashedFormPropertyName);
            }
        }
        return result;
    }

    private static <T> T bind(String result, Consumer consumer, Class<T> clazz) {
        Object obj = null;
        if (Boolean.class == clazz) {
            obj = Boolean.parseBoolean(result);
        } else if (String.class == clazz) {
            obj = result;
        } else if (Integer.class == clazz) {
            obj = Integer.valueOf(result);
        } else if (Long.class == clazz) {
            obj = Long.valueOf(result);
        } else if (List.class == clazz || Set.class == clazz) {
            if (StringUtils.isNotBlank(result)) {
                List<String> strings = new ArrayList<>();
                for (String s : result.split(",")) {
                    if (StringUtils.isNotBlank(s)) {
                        strings.add(s.trim());
                    }
                }
                obj = Set.class == clazz ? new HashSet<>(strings) : strings;
            } else {
                obj = Set.class == clazz ? Collections.emptySet() : Collections.emptyList();
            }
        }
        if (obj == null) {
            throw new UnsupportedOperationException("Class " + clazz.getName() + " is not supported in bind property!");
        }
        consumer.accept(obj);
        return (T) obj;
    }

}